package huzh.foundation.util;

import huzh.foundation.startup.AppEnv;

/**
 *<p>title:PasswordDigester</p>
 *<p>description:</p>
 * @since 2014-6-24
 * @author huzh
 * @version 1.0
 */
public class PasswordDigester {
    /**DIGEST_LENGTH*/
    private static final int DIGEST_LENGTH = 64;
    /**K*/
    static final long[] K = { 4794697086780616226L, 8158064640168781261L, -5349999486874862801L, -1606136188198331460L, 
        4131703408338449720L, 6480981068601479193L, -7908458776815382629L, -6116909921290321640L, -2880145864133508542L, 
        1334009975649890238L, 2608012711638119052L, 6128411473006802146L, 8268148722764581231L, -9160688886553864527L,
        -7215885187991268811L, -4495734319001033068L, -1973867731355612462L, -1171420211273849373L, 1135362057144423861L, 
        2597628984639134821L, 3308224258029322869L, 5365058923640841347L, 6679025012923562964L, 8573033837759648693L,
        -7476448914759557205L, -6327057829258317296L, -5763719355590565569L, -4658551843659510044L, -4116276920077217854L, 
        -3051310485924567259L, 489312712824947311L, 1452737877330783856L, 2861767655752347644L, 3322285676063803686L, 
        5560940570517711597L, 5996557281743188959L, 7280758554555802590L, 8532644243296465576L, -9096487096722542874L, 
        -7894198246740708037L, -6719396339535248540L, -6333637450476146687L, -4446306890439682159L, -4076793802049405392L, 
        -3345356375505022440L, -2983346525034927856L, -860691631967231958L, 1182934255886127544L, 1847814050463011016L, 
        2177327727835720531L, 2830643537854262169L, 3796741975233480872L, 4115178125766777443L, 5681478168544905931L, 
        6601373596472566643L, 7507060721942968483L, 8399075790359081724L, 8693463985226723168L, -8878714635349349518L, 
        -8302665154208450068L, -8016688836872298968L, -6606660893046293015L, -4685533653050689259L, -4147400797238176981L, 
        -3880063495543823972L, -3348786107499101689L, -1523767162380948706L, -757361751448694408L, 500013540394364858L, 
        748580250866718886L, 1242879168328830382L, 1977374033974150939L, 2944078676154940804L, 3659926193048069267L, 
        4368137639120453308L, 4836135668995329356L, 5532061633213252278L, 6448918945643986474L, 6902733635092675308L, 
        7801388544844847127L };
    /**H1*/
    protected long H1;
    /**H2*/
    protected long H2;
    /**H3*/
    protected long H3;
    /**H4*/
    protected long H4;
    /**H5*/
    protected long H5;
    /**H6*/
    protected long H6;
    /**H7*/
    protected long H7;
    /**H8*/
    protected long H8;
    /**W*/
    private long[] W = new long[80];
    /**xBuf*/
    private byte[] xBuf;
    /**wOff*/
    private int wOff;
    /**xBufOff*/
    private int xBufOff;
    /**byteCount1*/
    private long byteCount1;
    /**byteCount2*/
    private long byteCount2;

    /**
     * 
     */
    protected PasswordDigester(){
        this.xBuf = new byte[8];
        this.xBufOff = 0;
        reset();
    }

    /**
     * @param pd
     */
    protected PasswordDigester(PasswordDigester pd){
        this.xBuf = new byte[pd.xBuf.length];
        System.arraycopy(pd.xBuf, 0, this.xBuf, 0, pd.xBuf.length);
        this.xBufOff = pd.xBufOff;
        this.byteCount1 = pd.byteCount1;
        this.byteCount2 = pd.byteCount2;
        this.H1 = pd.H1;
        this.H2 = pd.H2;
        this.H3 = pd.H3;
        this.H4 = pd.H4;
        this.H5 = pd.H5;
        this.H6 = pd.H6;
        this.H7 = pd.H7;
        this.H8 = pd.H8;
        System.arraycopy(pd.W, 0, this.W, 0, pd.W.length);
        this.wOff = pd.wOff;
    }

    /**
     * @param paramArrayOfByte
     * @return
     */
    public static byte[] digest(byte[] bts){
        PasswordDigester pd = new PasswordDigester();
        byte[] resBt = new byte[pd.getDigestSize()];
        pd.update(bts, 0, bts.length);
        pd.doFinal(resBt, 0);
        return resBt;
    }

    /**
     * @return
     */
    public int getDigestSize(){
        return DIGEST_LENGTH;
    }

    /**
     * @param bts
     * @param len
     * @return
     */
    public int doFinal(byte[] bts, int len){
        finish();
        unpackWord(this.H1, bts, len);
        unpackWord(this.H2, bts, len + 8);
        unpackWord(this.H3, bts, len + 16);
        unpackWord(this.H4, bts, len + 24);
        unpackWord(this.H5, bts, len + 32);
        unpackWord(this.H6, bts, len + 40);
        unpackWord(this.H7, bts, len + 48);
        unpackWord(this.H8, bts, len + 56);
        reset();
        return DIGEST_LENGTH;
    }

    /**
     * 
     */
    public void finish() {
        adjustByteCounts();
        long l1 = this.byteCount1 << 3;
        long l2 = this.byteCount2;
        update((byte) -128);
        while (this.xBufOff != 0)
            update((byte) 0);
        processLength(l1, l2);
        processBlock();
    }

    /**
     * 
     */
    public void reset(){
        this.byteCount1 = 0L;
        this.byteCount2 = 0L;
        this.xBufOff = 0;
        for (int i = 0; i < this.xBuf.length; i++)
            this.xBuf[i] = 0;
        this.wOff = 0;
        for (int i = 0; i != this.W.length; i++)
            this.W[i] = 0L;
        this.H1 = 7640891576956012808L;
        this.H2 = -4942790177534073029L;
        this.H3 = 4354685564936845355L;
        this.H4 = -6534734903238641935L;
        this.H5 = 5840696475078001361L;
        this.H6 = -7276294671716946913L;
        this.H7 = 2270897969802886507L;
        this.H8 = 6620516959819538809L;
    }

    /**
     * @param bt
     */
    public void update(byte bt) {
        this.xBuf[(this.xBufOff++)] = bt;
        if (this.xBufOff == this.xBuf.length) {
            processWord(this.xBuf, 0);
            this.xBufOff = 0;
        }
        this.byteCount1 += 1L;
    }

    public void update(byte[] bts, int idx1, int idx2){
        while ((this.xBufOff != 0) && (idx2 > 0)){
            update(bts[idx1]);
            idx1++;
            idx2--;
        }
        while (idx2 > this.xBuf.length){
            processWord(bts, idx1);
            idx1 += this.xBuf.length;
            idx2 -= this.xBuf.length;
            this.byteCount1 += this.xBuf.length;
        }
        while (idx2 > 0) {
            update(bts[idx1]);
            idx1++;
            idx2--;
        }
    }

    protected void processBlock()
    {
      adjustByteCounts();
      for (int i = 16; i <= 79; i++)
        this.W[i] = (Sigma1(this.W[(i - 2)]) + this.W[(i - 7)] + Sigma0(this.W[(i - 15)]) + this.W[(i - 16)]);
      long l1 = this.H1;
      long l2 = this.H2;
      long l3 = this.H3;
      long l4 = this.H4;
      long l5 = this.H5;
      long l6 = this.H6;
      long l7 = this.H7;
      long l8 = this.H8;
      for (int j = 0; j <= 79; j++)
      {
        long l9 = l8 + Sum1(l5) + Ch(l5, l6, l7) + K[j] + this.W[j];
        long l10 = Sum0(l1) + Maj(l1, l2, l3);
        l8 = l7;
        l7 = l6;
        l6 = l5;
        l5 = l4 + l9;
        l4 = l3;
        l3 = l2;
        l2 = l1;
        l1 = l9 + l10;
      }
      this.H1 += l1;
      this.H2 += l2;
      this.H3 += l3;
      this.H4 += l4;
      this.H5 += l5;
      this.H6 += l6;
      this.H7 += l7;
      this.H8 += l8;
      this.wOff = 0;
      for (int j = 0; j != this.W.length; j++)
        this.W[j] = 0L;
    }

    protected void processLength(long paramLong1, long paramLong2)
    {
      if (this.wOff > 14)
        processBlock();
      this.W[14] = paramLong2;
      this.W[15] = paramLong1;
    }

    protected void processWord(byte[] paramArrayOfByte, int paramInt)
    {
      this.W[(this.wOff++)] = ((paramArrayOfByte[paramInt] & 0xFF) << 56 | (paramArrayOfByte[(paramInt + 1)] & 0xFF) << 48 | (paramArrayOfByte[(paramInt + 2)] & 0xFF) << 40 | (paramArrayOfByte[(paramInt + 3)] & 0xFF) << 32 | (paramArrayOfByte[(paramInt + 4)] & 0xFF) << 24 | (paramArrayOfByte[(paramInt + 5)] & 0xFF) << 16 | (paramArrayOfByte[(paramInt + 6)] & 0xFF) << 8 | paramArrayOfByte[(paramInt + 7)] & 0xFF);
      if (this.wOff == 16)
        processBlock();
    }

    protected void unpackWord(long paramLong, byte[] paramArrayOfByte, int paramInt)
    {
      paramArrayOfByte[paramInt] = (byte)(int)(paramLong >>> 56);
      paramArrayOfByte[(paramInt + 1)] = (byte)(int)(paramLong >>> 48);
      paramArrayOfByte[(paramInt + 2)] = (byte)(int)(paramLong >>> 40);
      paramArrayOfByte[(paramInt + 3)] = (byte)(int)(paramLong >>> 32);
      paramArrayOfByte[(paramInt + 4)] = (byte)(int)(paramLong >>> 24);
      paramArrayOfByte[(paramInt + 5)] = (byte)(int)(paramLong >>> 16);
      paramArrayOfByte[(paramInt + 6)] = (byte)(int)(paramLong >>> 8);
      paramArrayOfByte[(paramInt + 7)] = (byte)(int)paramLong;
    }

    private long Ch(long paramLong1, long paramLong2, long paramLong3)
    {
      return paramLong1 & paramLong2 ^ (paramLong1 ^ 0xFFFFFFFF) & paramLong3;
    }

    private long Maj(long paramLong1, long paramLong2, long paramLong3)
    {
      return paramLong1 & paramLong2 ^ paramLong1 & paramLong3 ^ paramLong2 & paramLong3;
    }

    private long Sigma0(long paramLong)
    {
      return rotateRight(paramLong, 1) ^ rotateRight(paramLong, 8) ^ paramLong >>> 7;
    }

    private long Sigma1(long paramLong)
    {
      return rotateRight(paramLong, 19) ^ rotateRight(paramLong, 61) ^ paramLong >>> 6;
    }

    private long Sum0(long paramLong)
    {
      return rotateRight(paramLong, 28) ^ rotateRight(paramLong, 34) ^ rotateRight(paramLong, 39);
    }

    private long Sum1(long paramLong)
    {
      return rotateRight(paramLong, 14) ^ rotateRight(paramLong, 18) ^ rotateRight(paramLong, 41);
    }

    private void adjustByteCounts()
    {
      if (this.byteCount1 > 2305843009213693951L)
      {
        this.byteCount2 += (this.byteCount1 >>> 61);
        this.byteCount1 &= 2305843009213693951L;
      }
    }

    private long rotateRight(long paramLong, int paramInt)
    {
      return paramLong >>> paramInt | paramLong << 64 - paramInt;
    }

    public static boolean compareHash(String paramString1, String paramString2)
    {
      return paramString1.equals(createHash(paramString2));
    }

    public static String createHash(String paramString)
    {
      String str = AppEnv.getParameter("JMFS");
      if ((str != null) && ("md5".equals(str.toLowerCase())))
      {
          Object localObject = new MD5();
        return ((MD5)localObject).getMD5ofStr(paramString);
      }
      byte[] localObject = digest(paramString.getBytes());
      byte[] arrayOfByte = Base64O.encode(localObject);
      return (String)new String(arrayOfByte);
    }

    public static String unHash(String paramString){
        byte[] arrayOfByte1 = digest(paramString.getBytes());
        byte[] arrayOfByte2 = Base64O.decode(arrayOfByte1);
        return new String(arrayOfByte2);
    }

    public static void main(String[] paramArrayOfString){
        String str1 = "123";
        String str2 = createHash(str1);
        System.out.println("en:" + str2);
        System.out.println("de:" + unHash("enenen"));
    }
}
